package edu.towson.cosc.cosc455.cannon.compiler;

import java.util.Stack;

public class MySemanticAnalyzer {
	
	//setting terminals
	final String lolcodeBegin = "#HAI";				final String lolcodeEnd = "#KTHXBYE";
	final String commentBegin = "#OBTW";			final String commentEnd = "#TLDR";
	final String headBegin = "#MAEK HEAD";			final String paragraphBegin = "#MAEK PARAGRAF";
	final String listBegin = "#MAEK LIST";			final String oicEnd = "#OIC";
	final String titleBegin = "#GIMMEH TITLE";		final String boldBegin = "#GIMMEH BOLD";
	final String italicsBegin = "#GIMMEH ITALICS";	final String itemBegin = "#GIMMEH ITEM";
	final String audioBegin = "#GIMMEH SOUNDZ";		final String videoBegin = "#GIMMEH VIDZ";
	final String newline = "#GIMMEH NEWLINE";		final String mkayEnd = "#MKAY";
	final String variableBegin = "#I HAS A";		final String variableDefine = "#ITZ";
	final String variableCall = "#VISIBLE";
	
	//declaring stacks
	Stack<String> stack1 = new Stack<String>();
	Stack<String> stack2 = new Stack<String>();
		
	public void generate() {
		//String to store the token
		String t;
		
		//putting everything from the parse tree into a stack
		for (String s : MyCompiler.parseTree)
			stack2.push(s);
		
		//putting everything into the other stack so it will be in order
		while (!stack2.isEmpty())
			stack1.push(stack2.pop());
		
		while (!stack1.isEmpty()) {
			t = stack1.pop();
			
			//if it doesn't begin with # it's text
			//so it's added without being converted to anything
			if (t.charAt(0) != '#')
				stack2.push(t);
			else {
				//adds the correct translation to convertedDoc
				//some extra steps are needed in cases like end tags that could be translated to different things
				switch (t.toUpperCase()) {
			
				case lolcodeBegin :
					stack2.push("<html>");
					break;
					
				case lolcodeEnd :
					stack2.push("</html>");
					removeVariable();
					break;
					
				case commentBegin :
					stack2.push("<!--");
					break;
					
				case commentEnd :
					stack2.push("-->");
					break;
					
				//if an end tag can be multiple things the head is pushed into a different stack
				case headBegin :
					stack2.push("<head>");
					break;
					
				case paragraphBegin :
					stack2.push("<p>");
					break;
					
				case listBegin :
					stack2.push("<ul>");
					break;
					
				case titleBegin :
					stack2.push("<title>");
					break;
					
				case boldBegin :
					stack2.push("<b>");
					break;
					
				case italicsBegin :
					stack2.push("<i>");
					break;
					
				case itemBegin :
					stack2.push("<li>");
					break;
					
				case newline :
					stack2.push("<br>");
					break;
					
				case oicEnd :
					getOICEnd();
					break;
					
				case mkayEnd :
					getMKAYEnd();
					break;
					
				case audioBegin :
					audio();
					break;
					
				case videoBegin :
					video();
					break;
					
				case variableBegin :
					setVariable();
					break;
					
				case variableCall :
					callVariable(stack1.pop());
					break;
				}
			}
		}
		while (!stack2.isEmpty())
			stack1.push(stack2.pop());
		while (!stack1.isEmpty())
			MyCompiler.convertedDoc.add(stack1.pop());
	}
	
	private void getOICEnd() {
		boolean found = false;
		String t;
		int i = 0;
		boolean ulEnd = false;
		while (!found) {
			t = stack2.pop();
			
			if (t.equals("<head>")) {
				stack2.push(t);
				for (int j=0; j<i; j++)
					stack2.push(stack1.pop());
				stack2.push("</head>");
				found = true;
			}
			else if (t.equals("<p>")) {
				stack2.push(t);
				for (int j=0; j<i; j++)
					stack2.push(stack1.pop());
				stack2.push("</p>");
				removeVariable();
				found = true;
			}
			else if (t.equals("<ul>") && !ulEnd) {
				stack2.push(t);
				for (int j=0; j<i; j++)
					stack2.push(stack1.pop());
				stack2.push("</ul>");
				found = true;
			}
			else if (t.equals("<ul>") && ulEnd) {
				ulEnd = false;
				stack1.push(t);
				i++;
			}
			else if (t.equals("</ul>")) {
				ulEnd = true;
				stack1.push(t);
				i++;
			}
			else {
				stack1.push(t);
				i++;
			}
		}
	}
	
	private void getMKAYEnd() {
		boolean found = false;
		String t;
		int i = 0;
		while (!found) {
			t = stack2.pop();
			
			if (t.equals("<title>")) {
				stack2.push(t);
				for (int j=0; j<i; j++)
					stack2.push(stack1.pop());
				stack2.push("</title>");
				found = true;
			}
			else if (t.equals("<b>")) {
				stack2.push(t);
				for (int j=0; j<i; j++)
					stack2.push(stack1.pop());
				stack2.push("</b>");
				removeVariable();
				found = true;
			}
			else if (t.equals("<i>")) {
				stack2.push(t);
				for (int j=0; j<i; j++)
					stack2.push(stack1.pop());
				stack2.push("</i>");
				removeVariable();
				found = true;
			}
			else if (t.equals("<li>")) {
				stack2.push(t);
				for (int j=0; j<i; j++)
					stack2.push(stack1.pop());
				stack2.push("</li>");
				found = true;
			}
			else {
				stack1.push(t);
				i++;
			}
		}
	}
	
	//first adds the begin tag for audio, <audio controls>
	//then adds an edited version the text for formatting purposes
	//then adds the end tag for audio, </audio>
	private void audio() {
		stack2.push("<audio controls>");
		String t = "<source src=\"" + stack1.pop() + "\">";
		stack2.push(t);
		stack1.pop();
		stack2.push("</audio>");
	}
	
	//basically does the same as audio but puts it in a different format
	private void video() {
		String t = "<iframe src=\"" + stack1.pop() + "\"/>";
		stack2.push(t);
		stack1.pop();
	}
	
	private void setVariable() {
		stack2.push("#" + stack1.pop());
		stack1.pop();
		stack2.push(stack1.pop());
		stack1.pop();
	}
	
	private void callVariable(String name) {
		String value;
		boolean found = false;
		int i = 0;
		while (!found && !stack2.isEmpty()) {
			if (stack2.peek().equals("#" + name)) {
				value = stack1.pop();
				stack2.push(value);
				for (int j=1; j<i; j++)
					stack2.push(stack1.pop());
				stack2.push(value);
				stack1.pop();
				found = true;
			}
			else {
				stack1.push(stack2.pop());
				i++;
			}
		}
		if (!found) {
			System.err.println("Semantic error! The variable \"" + name + "\" was not declared!");
			System.exit(0);
		}
	}
	
	private void removeVariable() {
		if (stack2.peek().equals("</b>")) {
			String placeholder = stack1.peek();
			while (!stack2.peek().equals("<b>"))
				stack1.push(stack2.pop());
			while (!stack1.peek().equals(placeholder)) {
				if (stack1.peek().charAt(0) == '#') {
					stack1.pop();
					stack1.pop();
				}
				else
					stack2.push(stack1.pop());
			}
		}
		else if (stack2.peek().equals("</i>")) {
			String placeholder = stack1.peek();
			while (!stack2.peek().equals("<i>"))
				stack1.push(stack2.pop());
			while (!stack1.peek().equals(placeholder)) {
				if (stack1.peek().charAt(0) == '#') {
					stack1.pop();
					stack1.pop();
				}
				else
					stack2.push(stack1.pop());
			}
		}
		else if (stack2.peek().equals("</p>")) {
			String placeholder = stack1.peek();
			while (!stack2.peek().equals("<p>"))
				stack1.push(stack2.pop());
			while (!stack1.peek().equals(placeholder)) {
				if (stack1.peek().charAt(0) == '#') {
					stack1.pop();
					stack1.pop();
				}
				else
					stack2.push(stack1.pop());
			}
		}
		else if (stack2.peek().equals("</html>")) {
			while (!stack2.peek().equals("<html>"))
				stack1.push(stack2.pop());
			while (!stack1.isEmpty()) {
				if (stack1.peek().charAt(0) == '#') {
					stack1.pop();
					stack1.pop();
				}
				else
					stack2.push(stack1.pop());
			}
		}
	}
}